package com.plugin.sharding;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;

import com.dangdang.ddframe.rdb.sharding.api.ShardingDataSourceFactory;
import com.dangdang.ddframe.rdb.sharding.api.rule.DataSourceRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.ShardingRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.TableRule;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.TableShardingStrategy;

public class DataSourceManagerBuilder {
	private static final String DATA_SOURCE = "dataSource";
	private Integer shardingTimes; // 表分片数，默认从0开始，共16个表
	private String driverClassName;
	private String url;
	private String username;
	private String password;
	private Class<? extends DataSource> dataSourceType; // 还是要回归到这个
	private List<ShardingTable> shardingTables;
	
	public static DataSourceManagerBuilder create() {
		return new DataSourceManagerBuilder();
	}
	
	public DataSourceManager build() {
		DataSource originDataSource = initOriginalDataSource();
		
		DataSourceRule dataSourceRule = initDataSourceRule(originDataSource);
		// 分片规则
		Collection<TableRule> tableRules = initTableRules(dataSourceRule);
		// 
		ShardingRule shardingRule = initShardingRule(tableRules, dataSourceRule);
		
		DataSource shardingDataSource = ShardingDataSourceFactory.createDataSource(shardingRule);
		
		return new DataSourceManager(shardingDataSource);
	}
	
	public DataSourceManagerBuilder driverClassName(String driverClassName) {
		this.driverClassName = driverClassName;
		return this;
	}
	
	public DataSourceManagerBuilder url(String url) {
		this.url = url;
		return this;
	}
	
	public DataSourceManagerBuilder username(String username) {
		this.username = username;
		return this;
	}
	
	public DataSourceManagerBuilder password(String password) {
		this.password = password;
		return this;
	}
	
	public DataSourceManagerBuilder dataSourceType(Class<? extends DataSource> dataSourceType) {
		this.dataSourceType = dataSourceType;
		return this;
	}
	
	public DataSourceManagerBuilder shardingTables(List<ShardingTable> shardingTables) {
		this.shardingTables = shardingTables;
		return this;
	}
	
	/**
	 * 暂时只有一个按表hashcode 取模来做分表的规则
	 * @param shardingTimes
	 * @return
	 */
	public DataSourceManagerBuilder shardingTimes(int shardingTimes) {
		this.shardingTimes = shardingTimes;
		return this;
	}
	
	private DataSource initOriginalDataSource() {
		if(StringUtils.isEmpty(driverClassName)) {
			throw new RuntimeException("driverClassName is null , user method \"driverClassName\" to set one");
		}
		if(StringUtils.isEmpty(url)) {
			throw new RuntimeException("url is null , user method \"url\" to set one");
		}
		if(StringUtils.isEmpty(username)) {
			throw new RuntimeException("username is null , user method \"username\" to set one");
		}
		if(StringUtils.isEmpty(password)) {
			throw new RuntimeException("password is null , user method \"password\" to set one");
		}
		
		// TODO 测试用
		BasicDataSource s = new BasicDataSource();
		s.setUrl(url);
		s.setDriverClassName(driverClassName);
		s.setUsername(username);
		s.setPassword(password);
		return s;
		
//		TODO 
//		如果使用spring builder工具去加载DataSource，它是直接去容器里面搜索的而不是真的去创建
//		return DataSourceBuilder.create()
//        		.driverClassName(driverClassName)
//        		.url(url)
//        		.username(username)
//        		.password(password)
//        		.type(dataSourceType == null ? BasicDataSource.class : dataSourceType)
//        		.build();
	}
	
	private DataSourceRule initDataSourceRule(DataSource dataSource) {
		Map<String, DataSource> dataSourceMap = new HashMap<String, DataSource>();
		dataSourceMap.put(DATA_SOURCE, dataSource);
		DataSourceRule rule = new DataSourceRule(dataSourceMap);
		
		return rule;
	}
	
	private List<TableRule> initTableRules(DataSourceRule dataSourceRule) {
		List<TableRule> tableRules = new ArrayList<TableRule>();
		if(shardingTables == null) {
			throw new RuntimeException("shardingTables not null, use method \"shardingTables\" to set");
		}
		for (ShardingTable shardingTable : shardingTables) {
			TableRule tableRule = genTableRule(shardingTable, dataSourceRule);
			tableRules.add(tableRule);
		}
		return tableRules;
	}

	
	private TableRule genTableRule(ShardingTable shardingTable, DataSourceRule dataSourceRule) {
		if(shardingTimes == null || shardingTimes.intValue() <= 0) {
			throw new RuntimeException("shardingTimes not null or zero, use method \"shardingTimes\" to set");
		}
		TableShardingStrategy shardingStrategy = 
				new TableShardingStrategy(shardingTable.getShardingColumn(), shardingTable.getShardingAlgorithm());
		List<String> actualTables = new ArrayList<String>();
		for (int i = 0; i <= shardingTimes; i++) {
			actualTables.add(shardingTable.getLogicTableName() + SymbolConstant.UDNER_LINE +i);
		}
		
		TableRule deviceInfoRule = TableRule.builder(shardingTable.getLogicTableName())
				.actualTables(actualTables)
				.dataSourceRule(dataSourceRule)
				.tableShardingStrategy(shardingStrategy)
				.build();
		
		return deviceInfoRule;
	
	}
	
	private ShardingRule initShardingRule(Collection<TableRule> tableRules, DataSourceRule dataSourceRule) {
		ShardingRule shardingRule = ShardingRule.builder()
				.tableRules(tableRules)
				.dataSourceRule(dataSourceRule)
//				.databaseShardingStrategy(databaseShardingStrategy) // databaseShardingStrategy是做数据库分片的
				.build();
		
		return shardingRule;
	}
}
